import Taro from '@tarojs/taro'
import {
  call, put, all, takeLatest, select
} from 'redux-saga/effects'
import api from '@API'
import {
  REGISTER,
  SAVE_PATIENT,
  GET_MY_PATIENTS,
  GET_PATIENT_RELATIONSHIP,
  GET_MY_PATIENTS_AND_REGISTRATION_STATUS,
  GET_PAIN_LOGS,
  GET_PATIENT_BY_ID,
  GET_LATEST_PAIN_LOG,
  CREATE_PAIN_LOG,
  SET_PAIN_LOCATION,
  SET_PAIN_HEAD_SITE,
  GET_PAIN_LOG_OPTIONS,
  SET_PAIN_LOG_SELECT_OPTION,
  SET_PAIN_LOG_PATIENT,
  GET_PAINLOG_HEAD_MAP,
  GET_MY_CONSULTATIONS,
  GET_RELATED_TAGS_BY_NAME,
  SET_CURRENT_HISTORY_OPTIONS,
  GET_DEFAULT_PATIENT, CLEAR_PAIN_LOG_SELECT_OPTION, SET_BODY_SITE, INIT_PAIN_DATA, GET_PAIN_LOG_DETAIL, SHOW_CREATE_LOG
} from '@ACTIONS/patient'
import { formatData, showToast } from '@UTILS/common'
import { DEFAULT_TAKE } from '@CONSTANTS/common'
import Navigate from '@NAVIGATION/navigateManager'
import { PAIN_OPTIONS_TYPE_MAP, PAIN_OTHER_TYPE_MAP } from '@CONSTANTS/pain'
import { PATIENT_REGISTRATION_STATUS, PATIENT_TYPES } from '@CONSTANTS/patient'
import { START_PAIN_MANAGEMENT, UPDATE_PAIN_MANAGEMENT } from '@ACTIONS/managementPlan'
import { SET_SELECT_PATIENT } from '@ACTIONS/servicePack'
import { GET_MONTH_LOG, GET_TODAY_PAIN_LOG } from '@ACTIONS/dataCenter'
import { Analytic_Type } from '@CONSTANTS/analytic'
import { getExtraSites, getManagementPlanDetail, getPatient, getUser } from '@SAGAS/dataSelect'
import { LANGUAGE, SUB_CHANNEL, ACQUIRE_CHANNEL } from '@CONSTANTS/app'
import { PATIENT_SITE } from '@CONSTANTS/painLog'
import dayjs from 'dayjs'
import {
  getPainLogPatient, getPainLog, getFormatPainLogs, getPainLocation, getPainHeadSites, getPainMedications,
  getPainTreatments, getPainMood, getPainRelief, getPainTriggers, getPainConcomitantSymptom, getPreLatestPainLog,
  getPreMyPatients, getPainSymptoms, getPainAuras, getPainSleep, getPainActivityLevel, getPainSessions
} from './dataSelect'

const selectGetPainMap = {
  [PAIN_OPTIONS_TYPE_MAP.ConcomitantSymptom]: getPainConcomitantSymptom,
  [PAIN_OPTIONS_TYPE_MAP.Triggers]: getPainTriggers,
  [PAIN_OPTIONS_TYPE_MAP.Relief]: getPainRelief,
  [PAIN_OPTIONS_TYPE_MAP.Mood]: getPainMood,
  [PAIN_OPTIONS_TYPE_MAP.Treatments]: getPainTreatments,
  [PAIN_OPTIONS_TYPE_MAP.Symptoms]: getPainSymptoms,
  [PAIN_OPTIONS_TYPE_MAP.Auras]: getPainAuras,
  [PAIN_OPTIONS_TYPE_MAP.Sessions]: getPainSessions,
}

const getNextMedications = (prevMedications, displayText) => {
  const alreadyHas = prevMedications.findIndex(i => i.zh === displayText.zh) > -1
  const painMedications = alreadyHas
    ? prevMedications.filter(i => i.zh !== displayText.zh)
    : [...prevMedications, displayText]
  const noneArr = alreadyHas ? [] : [{ zh: '无', en: 'None' }]
  const isNone = displayText.en === 'None'
  return isNone ? noneArr : painMedications.filter(i => i.en !== 'None')
}

const getNextList = (prev, otherReason, item, isOtherOption) => {
  const isOther = item === 'Other'
  const isNone = item === 'None'
  const alreadyHas = prev.includes(item)
  let a
  if (isOther && !isOtherOption) {
    if (!otherReason) {
      a = alreadyHas ? prev.filter(i => i !== item) : [...prev, item]
    } else {
      a = alreadyHas ? prev : [...prev, item]
    }
  } else {
    a = alreadyHas ? prev.filter(i => i !== item) : [...prev, item]
  }
  const noneArr = alreadyHas ? [] : ['None']
  return isNone ? noneArr : a.filter(i => i !== 'None')
}

const savePatient = function* savePatient(action) {
  try {
    const {
      payload: {
        isEdit, isFromPainLog = false, doctorId: DoctorUserId, isCreateBasePatient, isFromServicePack, servicePackId,
        fromPlan, tId, isSurveyPatient, isPay, trackData, isChatRoom, btnType, isElectronic
      }, payload
    } = action
    const preMyPatients = yield select(getPreMyPatients)
    const result = yield call(api.savePatient, payload)
    yield put({ type: SAVE_PATIENT.SUCCESS, result, isEdit })
    const { PatientId } = result
    const isNewPatient = !preMyPatients.find(item => item.p8Id === PatientId)
    // 新就诊人更新默认患者
    if (isNewPatient || result.DefaultPatientEnabled) {
      yield put({ type: GET_DEFAULT_PATIENT.REQUEST })
    }

    if (isSurveyPatient) {
      yield call(api.registerFromSurveyQrCode, {
        PatientId,
        DoctorUserId,
        PatientType: PATIENT_TYPES[2],
        Register: true,
      })
      Navigate.redirectTo('surveyDetail', { tId, doctorId: DoctorUserId, patientId: PatientId })
      return
    }

    if (isCreateBasePatient) {
      yield put({ type: REGISTER.REQUEST, payload: { p8Id: PatientId, DoctorUserId, PatientType: PATIENT_TYPES[2] }, isElectronic })
      return
    }

    if (isFromPainLog || result.HasPainLog) {
      yield put({ type: SET_PAIN_LOG_PATIENT.REQUEST, result })
    }

    if (result.DefaultPatientEnabled) {
      yield put({ type: GET_MY_PATIENTS.REQUEST })
    }

    if (isFromServicePack) {
      yield put({ type: SET_SELECT_PATIENT.REQUEST, payload: { patient: result, pId: servicePackId } })
    }
    if (fromPlan && tId) {
      const planParams = { TemplateId: tId, PatientId }
      !isChatRoom && DoctorUserId && (planParams.DoctorUserId = DoctorUserId)
      const { prevScene, requestId, scene } = trackData || {}
      const planDetail = yield select(getManagementPlanDetail)
      const user = yield select(getUser)
      const obj = { title: planDetail.Title?.[LANGUAGE.ZH] }
      DoctorUserId && (obj.DoctorId = DoctorUserId)
      DoctorUserId && (obj.UserId = user.p8Id)
      const sceneData = requestId ? { PrevScene: prevScene, RequestId: requestId, Scene: scene } : null
      if (isPay) {
        Taro.tracker.trackEvent(Analytic_Type.tap_plan_buy.eventId, obj, sceneData)
        const params = { tId, patientId: PatientId, doctorId: DoctorUserId, requestId, isChatRoom }
        Navigate.planPayment(params)
      } else {
        Taro.tracker.trackEvent(Analytic_Type.tap_start_plan.eventId, obj, sceneData)
        showToast('保存成功')
        yield put({ type: START_PAIN_MANAGEMENT.REQUEST, payload: planParams, isSavePatient: true })
      }
      return
    }
    Taro.showToast({
      title: '保存成功',
      icon: 'success',
      duration: 1000,
      mask: true,
    })
    if (isFromPainLog && !fromPlan) {
      // yield handlePainLog(btnType)
      Navigate.navigateBack()
    } else {
      setTimeout(Taro.navigateBack, 1000)
    }
  } catch (error) {
    Taro.showToast({
      icon: 'none',
      title: error.message || error
    })
    yield put({ type: SAVE_PATIENT.FAILURE, error })
  }
}

const getMyPatients = function* getMyPatients(action) {
  try {
    const { payload, callBack } = action
    const result = yield call(api.getMyPatients, payload)
    yield put({ type: GET_MY_PATIENTS.SUCCESS, result })
    const prePainLogPatient = yield select(getPainLogPatient)
    const painLogPatient = result?.find(item => item.HasPainLog) || null
    callBack?.(result, painLogPatient)
    if (painLogPatient && painLogPatient.p8Id !== prePainLogPatient?.p8Id) {
      yield put({ type: SET_PAIN_LOG_PATIENT.REQUEST, result: painLogPatient })
    }
  } catch (error) {
    yield put({ type: GET_MY_PATIENTS.FAILURE, error })
  }
}

const getLatestPainLog = function* getLatestPainLog(action) {
  try {
    const { payload } = action
    const { isCreatePlainLog, loadOptions, site } = payload || {}
    const position = site === PATIENT_SITE.Head ? PATIENT_SITE.Head : 'Body'
    const data = yield call(api.getLatestPainLog, { position })
    const { LatestPainLog: result, Total } = data || {}
    const latestPainLog = yield select(getPreLatestPainLog)
    if (latestPainLog?.PainLogId !== result?.PainLogId || loadOptions) {
      yield put({ type: GET_LATEST_PAIN_LOG.SUCCESS, result: result?.PainLogId ? result : null })
    }
    isCreatePlainLog && (yield put({ type: CLEAR_PAIN_LOG_SELECT_OPTION.REQUEST }))
    loadOptions && (yield put({ type: GET_PAIN_LOG_OPTIONS.REQUEST, payload: { site } }))
  } catch (error) {
    yield put({ type: GET_MY_PATIENTS.FAILURE, error })
  }
}

const getMyPatientsAndRegistrationStatus = function* getMyPatientsAndRegistrationStatus(action) {
  try {
    const { payload } = action
    const result = yield call(api.getMyPatientsAndRegistrationStatus, payload)
    const canContactDocPatientList = result.filter(item => (item.RegistrationStatus === PATIENT_REGISTRATION_STATUS.Active))
    yield put({ type: GET_MY_PATIENTS_AND_REGISTRATION_STATUS.SUCCESS, result, canContactDocPatientList })
  } catch (error) {
    yield put({ type: GET_MY_PATIENTS_AND_REGISTRATION_STATUS.FAILURE, error })
  }
}

const getPatientRelationship = function* getPatientRelationship() {
  try {
    const relation = yield call(api.getPatientRelationship)
    yield put({ type: GET_PATIENT_RELATIONSHIP.SUCCESS, relation })
  } catch (error) {
    yield put({ type: GET_PATIENT_RELATIONSHIP.FAILURE, error })
  }
}

const register = function* register(action) {
  const { payload, complete, isElectronic } = action
  try {
    const { isRegister = false } = payload
    if (isElectronic) {
      payload.AcquireChannel = ACQUIRE_CHANNEL.DoctorQRCode
      payload.SubChannel = SUB_CHANNEL.Electronic
    }

    const { PatientId, Status, SessionId, DoctorDisplayName, DoctorUserId } = yield call(api.register, payload)
    yield put({ type: REGISTER.SUCCESS, pId: PatientId, status: Status })
    complete?.(true)
    if (isElectronic && !isRegister) {
      Navigate.medRecord({ doctorId: DoctorUserId, patientId: PatientId })
      return
    }
    Navigate.chatRoomPage({
      SessionId,
      title: DoctorDisplayName[LANGUAGE.ZH]
    })
  } catch (error) {
    complete?.(false)
    yield put({ type: REGISTER.FAILURE, error })
  }
}

const createPainLog = function* createPainLog(action) {
  try {
    const { payload, isPlanTask, payload: { OverallRating = 0, PainLogId, PatientId, DateBucket } } = action
    const url = PainLogId ? api.editPainLog : api.createPainLog
    const result = yield call(url, payload)
    const isPainLog = OverallRating > 0 // 疼痛日志， 为 0 表示无痛日志
    yield put({ type: UPDATE_PAIN_MANAGEMENT.REQUEST })
    showToast('记录成功')
    yield put({ type: CREATE_PAIN_LOG.SUCCESS, result, overallRating: OverallRating })
    yield put({ type: GET_MONTH_LOG.REQUEST, pId: PatientId, bucket: dayjs(DateBucket || Date.now()).format('YYYY-MM') })
    yield put({ type: SHOW_CREATE_LOG.SUCCESS, payload: { showCreateLog: false } })
  } catch (error) {
    yield put({ type: CREATE_PAIN_LOG.FAILURE, error })
  }
}

const setPainLocation = function* setPainLocation(action) {
  try {
    const { payload: { type } } = action
    const locations = yield select(getPainLocation)
    const otherLocations = locations.filter(l => l !== type)
    const result = locations.includes(type) ? otherLocations : [...otherLocations, type]
    yield put({ type: SET_PAIN_LOCATION.SUCCESS, result })
  } catch (error) {
    yield put({ type: SET_PAIN_LOCATION.FAILURE, error })
  }
}

const setPainHeadSite = function* setPainHeadSite(action) {
  try {
    const { payload: { type } } = action
    const headSites = yield select(getPainHeadSites)
    const otherHeadSites = headSites.filter(l => l !== type)
    const result = headSites.includes(type) ? otherHeadSites : [...otherHeadSites, type]
    yield put({ type: SET_PAIN_HEAD_SITE.SUCCESS, result })
  } catch (error) {
    yield put({ type: SET_PAIN_HEAD_SITE.FAILURE, error })
  }
}

const getPainLogs = function* getPainLogs(action) {
  try {
    const { payload: { skip, ...rest } } = action
    const prePainLogs = yield select(getPainLog)
    const preFormatPainLogs = yield select(getFormatPainLogs)
    const realSkip = skip === 0 ? 0 : prePainLogs.length
    const { Total, PainLogs } = yield call(api.getPainLogs, { ...rest, skip: realSkip, take: DEFAULT_TAKE })
    const painLogs = [...prePainLogs, ...PainLogs]

    yield put({
      type: GET_PAIN_LOGS.SUCCESS,
      painLogs,
      canLoadMore: painLogs.length < Total,
      formatPainLogs: formatData(PainLogs, preFormatPainLogs)
    })
  } catch (error) {
    yield put({ type: GET_PAIN_LOGS.FAILURE, error })
  }
}

const getPainLogHeadMap = function* getPainLogHeadMap() {
  try {
    const headMap = yield call(api.getPainLogOptions, { option: PAIN_OPTIONS_TYPE_MAP.HeadMap })
    yield put({ type: GET_PAINLOG_HEAD_MAP.SUCCESS, headMap })
  } catch (error) {
    yield put({ type: GET_PAINLOG_HEAD_MAP.FAILURE, error })
  }
}

const getPainLogOptions = function* getPainLogOptions(action) {
  try {
    const { payload } = action
    const { site = '', isImportLog } = payload || {}
    const {
      HeadMap: headMap,
      BodyMap: bodyMap,
      Symptoms: symptoms,
      Triggers: triggers,
      Relief: relief,
      Treatments: treatments,
      DefaultMedicine: defaultMedicine,
      Mood: mood,
      Sleep: sleep,
      ConcomitantSymptom: concomitantSymptom,
      Auras: auras,
      ActivityLevel: activityLevel,
      Sessions: sessions
    } = yield call(api.getAllOptions, { site })
    const options = {
      bodyMap,
      symptoms,
      auras,
      concomitantSymptom,
      triggers,
      relief,
      treatments,
      defaultMedicine,
      mood,
      sleep,
      headMap,
      activityLevel,
      sessions
    }
    isImportLog && (yield put({ type: INIT_PAIN_DATA.SUCCESS, options }))
    yield put({
      type: GET_PAIN_LOG_OPTIONS.SUCCESS,
      ...options,
    })
  } catch (error) {
    yield put({ type: GET_PAIN_LOG_OPTIONS.FAILURE, error })
  }
}

const setPainLogSelectOption = function* setPainLogSelectOption(action) {
  try {
    const {
      payload: {
        item, displayText, type, otherReason
      }
    } = action

    if ([PAIN_OTHER_TYPE_MAP.Sleep, PAIN_OTHER_TYPE_MAP.ActivityLevel].includes(type)) {
      const prevSleep = yield select(getPainSleep)
      const prevActivityLevel = yield select(getPainActivityLevel)
      const isSleepAndAlreadyHas = PAIN_OTHER_TYPE_MAP.Sleep === type && prevSleep === item
      const isActivityAlreadyHas = PAIN_OTHER_TYPE_MAP.ActivityLevel === type && prevActivityLevel === item
      yield put({
        type: SET_PAIN_LOG_SELECT_OPTION.SUCCESS,
        [`pain${type}`]: (isSleepAndAlreadyHas || isActivityAlreadyHas) ? '' : item
      })
    } else if (PAIN_OTHER_TYPE_MAP[type] === type) {
      yield put({
        type: SET_PAIN_LOG_SELECT_OPTION.SUCCESS,
        [`pain${type}`]: item
      })
    } else if (type === PAIN_OPTIONS_TYPE_MAP.DefaultMedicine) {
      const prevMedications = yield select(getPainMedications)
      const painMedications = getNextMedications(prevMedications, displayText)
      yield put({
        type: SET_PAIN_LOG_SELECT_OPTION.SUCCESS,
        painMedications,
      })
    } else {
      const isOtherOption = [PAIN_OPTIONS_TYPE_MAP.Symptoms].includes(type)
      const prev = yield select(selectGetPainMap[type])
      const isOther = item === 'Other'
      const isNone = item === 'None'
      const nextList = getNextList(prev, otherReason, item, isOtherOption)

      yield put({
        type: SET_PAIN_LOG_SELECT_OPTION.SUCCESS,
        [`pain${type}`]: nextList,
        isOther,
        otherReason: isNone ? '' : otherReason,
        painType: type
      })
    }
  } catch (error) {
    yield put({ type: SET_PAIN_LOG_SELECT_OPTION.FAILURE, error })
  }
}

const getPatientById = function* getPatientById(action) {
  try {
    const { payload } = action
    const data = yield call(api.getPatientById, payload)
    yield put({ type: GET_PATIENT_BY_ID.SUCCESS, data })
  } catch (error) {
    yield put({ type: GET_PATIENT_BY_ID.FAILURE, error })
  }
}

const getMyConsultations = function* getMyConsultations(action) {
  try {
    const { payload } = action
    const data = yield call(api.getMyConsultations, payload)
    yield put({ type: GET_MY_CONSULTATIONS.SUCCESS, myConsultationsWithTotal: data })
  } catch (error) {
    yield put({ type: GET_MY_CONSULTATIONS.FAILURE, error })
  }
}

const getRelatedTagsByName = function* getRelatedTagsByName(action) {
  try {
    const { payload = {} } = action
    const { Tags } = yield call(api.getRelatedTagsByName, payload)
    const { Tags: Diagnosis } = yield call(api.getRelatedTagsByName, { name: '医生诊断' })
    yield put({ type: GET_RELATED_TAGS_BY_NAME.SUCCESS, currentHistoryTags: Tags, diagnosis: Diagnosis })
  } catch (error) {
    yield put({ type: GET_RELATED_TAGS_BY_NAME.FAILURE, error })
  }
}

const setCurrentHistoryOptions = function* setCurrentHistoryOptions(action) {
  try {
    const { payload: { selectedCurrentHistoryTags } = {} } = action
    yield put({ type: SET_CURRENT_HISTORY_OPTIONS.SUCCESS, selectedCurrentHistoryTags })
  } catch (error) {
    yield put({ type: SET_CURRENT_HISTORY_OPTIONS.FAILURE, error })
  }
}

const getDefaultPatient = function* getDefaultPatient() {
  try {
    const defaultPatient = yield call(api.getDefaultPatient)
    yield put({ type: GET_DEFAULT_PATIENT.SUCCESS, defaultPatient })
  } catch (error) {
    yield put({ type: GET_DEFAULT_PATIENT.FAILURE, error })
  }
}

const siteIsChange = ({ preLocations, preHeadSites, extraSites, painLocations }) => {
  const isBody = painLocations?.length > 0
  const preIsEmpty = !preLocations?.length && !preHeadSites.length
  const siteChange = preIsEmpty && extraSites?.length > 0
    || !isBody && preLocations?.length > 0
    || isBody && (preIsEmpty || preHeadSites.length > 0)
  return siteChange
}
const setBodySite = function* setBodySite(action) {
  try {
    const { data } = action
    const { painLocations } = data || {}
    const preLocations = yield select(getPainLocation)
    const preHeadSites = yield select(getPainHeadSites)
    const extraSites = yield select(getExtraSites)
    // const isBody = painLocations?.length > 0
    // const siteChange = siteIsChange({ preLocations, preHeadSites, extraSites, painLocations })
    yield put({ type: SET_BODY_SITE.SUCCESS, data })
    // siteChange && (yield put({ type: CLEAR_PAIN_LOG_SELECT_OPTION.REQUEST }))
    // siteChange && (yield put({ type: GET_PAIN_LOG_OPTIONS.REQUEST, payload: { site: isBody ? PATIENT_SITE.Other : PATIENT_SITE.Head } }))
  } catch (error) {
    yield put({ type: SET_BODY_SITE.FAILURE, error })
  }
}

const initPaintData = function* initPaintData() {
  const latestPainLog = yield select(getPreLatestPainLog)
  const { BodyMaps } = latestPainLog
  const preLocations = yield select(getPainLocation)
  const preHeadSites = yield select(getPainHeadSites)
  const extraSites = yield select(getExtraSites)
  const isBody = BodyMaps?.length > 0
  const siteChange = siteIsChange({ preLocations, preHeadSites, extraSites, painLocations: BodyMaps })
  if (siteChange) {
    yield put({ type: GET_PAIN_LOG_OPTIONS.REQUEST, payload: { site: isBody ? PATIENT_SITE.Other : PATIENT_SITE.Head, isImportLog: true } })
  } else {
    yield put({ type: INIT_PAIN_DATA.SUCCESS })
  }
}

const getPainLogDetail = function* getPainLogDetail(action) {
  const { payload } = action
  try {
    const painLogDetail = yield call(api.getPainLogDetail, payload)
    const result = painLogDetail?.PainLogId ? painLogDetail : null
    yield put({ type: GET_LATEST_PAIN_LOG.SUCCESS, result })
    yield put({ type: INIT_PAIN_DATA.SUCCESS, result })
  } catch (error) {
    yield put({ type: INIT_PAIN_DATA.FAILURE, error })
  }
}

export default function* rootSaga() {
  yield all([
    takeLatest(REGISTER.REQUEST, register),
    takeLatest(CREATE_PAIN_LOG.REQUEST, createPainLog),
    takeLatest(SET_PAIN_LOCATION.REQUEST, setPainLocation),
    takeLatest(SET_PAIN_HEAD_SITE.REQUEST, setPainHeadSite),
    takeLatest(SAVE_PATIENT.REQUEST, savePatient),
    takeLatest(GET_MY_PATIENTS.REQUEST, getMyPatients),
    takeLatest(GET_PATIENT_RELATIONSHIP.REQUEST, getPatientRelationship),
    takeLatest(GET_MY_PATIENTS_AND_REGISTRATION_STATUS.REQUEST, getMyPatientsAndRegistrationStatus),
    takeLatest(GET_PAIN_LOGS.REQUEST, getPainLogs),
    takeLatest(GET_PAIN_LOG_OPTIONS.REQUEST, getPainLogOptions),
    takeLatest(GET_PAINLOG_HEAD_MAP.REQUEST, getPainLogHeadMap),
    takeLatest(SET_PAIN_LOG_SELECT_OPTION.REQUEST, setPainLogSelectOption),
    takeLatest(GET_PATIENT_BY_ID.REQUEST, getPatientById),
    takeLatest(GET_LATEST_PAIN_LOG.REQUEST, getLatestPainLog),
    takeLatest(GET_MY_CONSULTATIONS.REQUEST, getMyConsultations),
    takeLatest(GET_RELATED_TAGS_BY_NAME.REQUEST, getRelatedTagsByName),
    takeLatest(SET_CURRENT_HISTORY_OPTIONS.REQUEST, setCurrentHistoryOptions),
    takeLatest(GET_DEFAULT_PATIENT.REQUEST, getDefaultPatient),
    takeLatest(SET_BODY_SITE.REQUEST, setBodySite),
    takeLatest(INIT_PAIN_DATA.REQUEST, initPaintData),
    takeLatest(GET_PAIN_LOG_DETAIL.REQUEST, getPainLogDetail),
  ])
}
